

;
; expr-statements utilities:
; mapping table: *expr-statements*, definition helper: def-expr-handler,
; caller helper: call-expr-handler
;
(setf *expr-statements* (make-hash-table))

(defmacro def-expr-handler (expr-name args &rest body)
  (assert (and (symbolp expr-name) (listp args)))
  `(progn
     (if (gethash ',expr-name *expr-statements*)
       (error (format nil "redefinition of ~a" expr-name)))
     (setf (gethash ',expr-name *expr-statements*)
           (lambda ,args ,@body))
     ))

(defun call-expr-handler (expr-name indent args)
  (assert (and (symbolp expr-name) (list args)))
  (let ((handler (gethash expr-name *expr-statements*)))
    (unless handler (error (format nil "expression handler '~a' is undefined"
                                   expr-name)))

    ; return the evaluated expression
    (eval (append (list 'funcall handler) (list indent) args))
    ))

;
; expression handler:
; (if predicate-expression then-statement &optional else-statement)
;
(def-expr-handler if (indent predicate-expression then-block &optional else-block)
                  (conc-string
                   "if( "
                   (format nil "~a" predicate-expression)
                   " )"

                   ; add mandatory then-block
                   *new-line*
                   (stringify-block-expression then-block indent)

                   ; add optional else-block
                   (if else-block
                       (conc-string *new-line*
                                    (stringify-block-expression
                                     else-block indent)
                                    )
                     "")
                   ))

;
; (return function-expression)
;
(def-expr-handler return (indent expr)
                  (conc-string
                   indent
                   "return"
                   " " ; delimeter
                   (stringify-expression expr "") ; expression statement
                   ";" ; comma delimeter at the end of statement
                  ))

;
; expression handler
;
;(def-expr-handler)


;
; stringifies atomic expression
;
(defun stringify-variable (var-expr indent)
  (assert (atom var-expr))
  (format nil "~a<~a>" indent var-expr)
  )
;
; stringifies expression being used in the function body
;
(defun stringify-expression (expr indent)
  (if (listp expr)
      (call-expr-handler (car expr) indent (cdr expr))
    (stringify-variable expr indent)
    ))

;
; stringifies block expression (i.e. operational structures being
; used in the function bodies)
;
(defun stringify-block-expression (block-expression indent-str)
  (assert (stringp indent-str))
  (let (
        (result indent-str) ; initialize result with indentation string
        (next-indent-str (conc-string indent-str *indentation-unit*))
        )
    ; add open brace
    (append-string result "{" *new-line*)

    ; add each expression
    (append-string result
                     (stringify-expression block-expression next-indent-str)
                     *new-line*
                     )

    ; add close brace
    (append-string result "}" *new-line*)

    ; return stringified block expression
    result
    )
  )

(def-ast-handler test (ast-context name)
  (format t "ast-handler-test (~a ~a)~%" ast-context name)
  (+ 1 2)
  )

(def-ast-handler func (ast-context
		       ret-type name args &key
		       ((:comment comment) nil)
		       ((:body body) nil)
                       ((:lang-name lang-name) name)
		       )
  (if comment (format t "// ~a~%" comment))
  (format t "// ast-context: ~a~%" ast-context)
  (format t "~a ~a~a~%"
	  (stringify-typename ret-type)
	  lang-name
	  (stringify-arg-list args))
  (if body
    (format t "~a" (stringify-block-expression body "")))
  )

(setf *test-form1* '(func int main (
                                    (int argc)
                                    ((ptr (ptr (const char))) argv)
                                    )
                          :lang-name "main"
                          :comment "defines entry in the console application"
                          :body (return 0)))

(setf *test-form2* '(func int foo ((long-long arg))
                          :comment "quite a simple foo function"
                          :body (return (+ arg 1))))

(defun test-seq0 ()
  (process-ast-list 23 *test-form1*)
  ;(process-ast-list 778 '(test "test.c"))
  )

